﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace Gmantis.Controls
{
    public class AggregatingTable<T, Aggregator> where Aggregator: struct, IAggregator<T>
    {
        // Fields
        private T[] _array;
        private int _count;
        private int _factor;

        // Methods
        public AggregatingTable(IEnumerable<T> lengths)
        {
            this.Init(10, lengths);
        }

        [Obsolete]
        public AggregatingTable(int factor, IEnumerable<T> lengths)
        {
            this.Init(factor, lengths);
        }

        private void Aggregate()
        {
            int num = this._count;
            int index = 0;
            int num3 = num;
            while (num > 1)
            {
                int num4 = num3;
                do
                {
                    int num5 = Math.Min(index + this._factor, num4);
                    Aggregator local2 = default(Aggregator);
                    T left = local2.Zero();
                    while (index < num5)
                    {
                        Aggregator local4 = default(Aggregator);
                        left = local4.Sum(left, this._array[index]);
                        index++;
                    }
                    this._array[num3] = left;
                    num3++;
                }
                while (index < num4);
                num = num3 - index;
            }
        }

        private int ArrayLength(int levelCount)
        {
            int num = 0;
            while (true)
            {
                num += levelCount;
                if (levelCount == 1)
                {
                    return num;
                }
                levelCount = ((levelCount + this._factor) - 1) / this._factor;
            }
        }

        private void Copy(IEnumerable<T> lengths)
        {
            int index = 0;
            foreach (T local in lengths)
            {
                this._array[index] = local;
                index++;
            }
        }

        public IndexOffset<T> FindByIndex(int index)
        {
            T left = default(Aggregator).Zero();
            int num = 0;
            int num2 = index;
            int num3 = this._count;
            while (num2 > 0)
            {
                int num4 = num2 % this._factor;
                int num5 = num2 / this._factor;
                for (int i = num2 - num4; i < num2; i++)
                {
                    Aggregator local4 = default(Aggregator);
                    left = local4.Sum(left, this._array[num + i]);
                }
                num += num3;
                num3 = ((num3 + this._factor) - 1) / this._factor;
                num2 = num5;
            }
            return new IndexOffset<T> { Index = index, Offset = left };
        }

        public IndexOffset<T> FindByLength(T length)
        {
            IndexOffset<T> offset = this.FindByLength(length, 0, this._count);
            if (offset.Index > this._count)
            {
                offset.Index = this._count;
            }
            return offset;
        }

        private IndexOffset<T> FindByLength(T length, int levelBegin, int levelCount)
        {
            IndexOffset<T> offset = new IndexOffset<T>();
            if (levelCount > this._factor)
            {
                offset = this.FindByLength(length, levelBegin + levelCount, ((levelCount + this._factor) - 1) / this._factor);
            }
            offset.Index *= this._factor;
            while (offset.Index < levelCount)
            {
                Aggregator local2 = default(Aggregator);
                T x = local2.Sum(offset.Offset, this._array[levelBegin + offset.Index]);
                Aggregator local5 = default(Aggregator);
                if (local5.Compare(x, length) > 0)
                {
                    return offset;
                }
                offset.Offset = x;
                offset.Index++;
            }
            return offset;
        }

        public T GetLength(int index)
        {
            return this._array[index];
        }

        private void Init(int factor, IEnumerable<T> lengths)
        {
            this._factor = factor;
            this._count = lengths.Count<T>();
            if (this._count > 0)
            {
                this._array = new T[this.ArrayLength(this._count)];
                this.Copy(lengths);
                this.Aggregate();
            }
        }

        public T TotalLength()
        {
            if (this._array == null)
            {
                Aggregator local2 = default(Aggregator);
                return local2.Zero();
            }
            return this._array[this._array.Length - 1];
        }

        public void Update(int index, T newLength)
        {
            int num = 0;
            int num2 = index;
            int num3 = this._count;
            T left = newLength;
            while (true)
            {
                this._array[num + num2] = left;
                if (num3 == 1)
                {
                    return;
                }
                int num4 = num2 % this._factor;
                int num5 = num2 / this._factor;
                Aggregator local2 = default(Aggregator);
                left = local2.Zero();
                for (int i = num2 - num4; i < Math.Min((num2 - num4) + this._factor, num3); i++)
                {
                    Aggregator local4 = default(Aggregator);
                    left = local4.Sum(left, this._array[num + i]);
                }
                num += num3;
                num3 = ((num3 + this._factor) - 1) / this._factor;
                num2 = num5;
            }
        }

        // Properties
        public int Count
        {
            get
            {
                return this._count;
            }
        }
    }

    public interface IAggregator<T>
    {
        // Methods
        int Compare(T x, T y);
        T Sum(T left, T right);
        T Zero();
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct IndexOffset<T>
    {
        public int Index;
        public T Offset;
    }
}
